package com.dreamchaser.familydevice.config.device;

import com.dreamchaser.familydevice.annotation.Function;
import com.dreamchaser.familydevice.config.device.infunction.IFunction;
import com.dreamchaser.familydevice.constant.DeviceCache;
import com.dreamchaser.familydevice.dao.ComStatusMapper;
import com.dreamchaser.familydevice.dao.ComponentMapper;
import com.dreamchaser.familydevice.domain.dto.DeviceExpression;
import com.dreamchaser.familydevice.service.IDeviceService;
import com.dreamchaser.familydevice.util.ClassUtils;
import com.dreamchaser.familydevice.util.device.ByteBufUtil;
import com.dreamchaser.familydevice.util.device.ByteHexUtil;
import com.dreamchaser.familydevice.util.device.DeviceDataUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;


import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/***
 *                    _ooOoo_
 *                   o8888888o
 *                   88" . "88
 *                   (| -_- |)
 *                    O\ = /O
 *                ____/`---'\____
 *                 .' \\| |// `.
 *               / \\||| : |||// \
 *             / _||||| -:- |||||- \
 *               | | \\\ - /// | |
 *             | \_| ''\---/'' | |
 *              \ .-\__ `-` ___/-. /
 *           ___`. .' /--.--\ `. . __
 *        ."" '< `.___\_<|>_/___.' >' "".
 *       | | : `- \`.;`\ _ /`;.`/ -` : | |
 *         \ \ `-. \_ __\ /__ _/ .-` / /
 * ======`-.____`-.___\_____/___.-`____.-'======
 *                    `=---='
 *
 * =============================================
 *          佛祖保佑             永无BUG
 *
 *
 * @Author: SJie
 * Date: 2020/8/17 13:09
 * Description: 表达式解析
 *
 */
@Component
@Slf4j
public class ExpressionAnalyse {

    @Autowired
    ComStatusMapper comStatusMapper;

    @Autowired
    ComponentMapper componentMapper;
    @Autowired
    IDeviceService deviceService;
    @Autowired
    StringRedisTemplate redisTemplate;

    public static HashMap<String, IFunction> functionMap;

    /**
     * 所有设备表达式解析初始化
     */
    @PostConstruct
    private void init() {
        initFunction();
        List<String> componentList = comStatusMapper.selectListVarName();
        log.debug("ComStatusVar size:" + componentList.size());
        DeviceCache.ComStatusVar = new HashSet<>(componentList);

        List<DeviceExpression> deviceExpressionList = componentMapper.selectListExpression();
        DeviceCache.ComExperssion = deviceExpressionList;
        analysisExpression();
        taskExecute();

    }

    /**
     * 初始化函数
     */
    private void initFunction() {
        functionMap = new HashMap<>();
        List<Class<?>> classList = ClassUtils.getClasses("com.dreamchaser.familydevice.config.device.infunction");
        for (Class<?> cl : classList) {
            Function function = cl.getDeclaredAnnotation(Function.class);
            if (function != null) {
                if (!functionMap.containsKey(function.value())) {
                    try {
                        functionMap.put(function.value(), (IFunction) cl.newInstance());
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                } else {
                    throw new RuntimeException("存在相同的内置函数定义");
                }

            }
        }
    }

    /**
     * 任务执行开始
     */
    private void taskExecute() {

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(15);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                while (true) {
                    for (Map.Entry<String, Integer> entry : DeviceCache.taskName.entrySet()) {
                        Object value = null;
                        try {
                            value = redisTemplate.opsForValue().get(entry.getKey());
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        if (value == null) {
                            int[] data = {entry.getValue()};
                            int deviceCode=0;
                            if (entry.getKey().startsWith("delay")) {
                                deviceCode = ByteHexUtil.hexStrToHex(entry.getKey().replace("delay", "").substring(0,2));
                                Channel channel = DeviceDataUtil.getDeviceChannel(deviceCode);
                                if (channel != null) {
                                    log.info(deviceCode + "设备task任务执行：" + ByteHexUtil.hexToHexStr(entry.getValue()));
                                    channel.writeAndFlush(ByteBufUtil.hexToByteBuf(data));
                                }
                            }else  if (entry.getKey().startsWith("init_delay")) {
                                redisTemplate.delete(entry.getKey().replaceFirst("init_delay", "delay_count"));
                            }


                            DeviceCache.taskName.remove(entry.getKey());
                        }

                    }
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }).start();
    }

    /**
     * 解析表达式
     */
    @Transactional(rollbackFor = Exception.class)
    public void analysisExpression() {
        List<DeviceExpression> deviceExpressionList = DeviceCache.ComExperssion;
        for (DeviceExpression deviceExpression : deviceExpressionList) {

            if (computeExpression(deviceExpression.getExpression())) {
                com.dreamchaser.familydevice.po.Component component = new com.dreamchaser.familydevice.po.Component();
                component.setId(deviceExpression.getMainComId());
                component.setStatusCode(deviceExpression.getStatusCode());
                componentMapper.updateById(component);
            }
        }

    }

    /**
     * 计算表达式
     *
     * @param expression
     * @return
     */
    private boolean computeExpression(String expression) {
        if (!StringUtils.isEmpty(expression)) {
            Pattern pattern = Pattern.compile("D\\d{4}");
            Matcher matcher = pattern.matcher(expression);
            while (matcher.find()) {
                String varName = matcher.group(0);
                String r = "0";
                if (DeviceCache.ComStatusVar.contains(varName)) {
                    r = "1";
                }
                expression = expression.replaceFirst(varName, r);
            }
            ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
            log.info("表达式：" + expression);
            try {
                int b = (int) jse.eval(expression);
                log.info("结果：" + b);
                return b == 1;
            } catch (Exception t) {
                t.printStackTrace();
            }
        }
        return false;

    }

    public void notifyDevice(int deviceCode, Channel channel) {
        for (DeviceExpression expression : DeviceCache.ComExperssion) {
            // 只计算与设备相关的表达式
            if (expression.getDeviceCode() == deviceCode) {
                if (computeExpression(expression.getExpression())) {
                    com.dreamchaser.familydevice.po.Component component = componentMapper.selectById(expression.getMainComId());
//                    if (component.getStatusCode() != expression.getStatusCode()) {

                        // 要发送的数据

                        int data = (expression.getMainComCode() << 4) + expression.getStatusCode();
                        if (StringUtils.isNotBlank(expression.getInternalFunc())) {
                            // 解析内置函数，延迟处理
                            log.info(deviceCode + "：解析内置函数");
                            if (!deviceService.analyseInternalFunc(deviceCode, expression.getInternalFunc(), data)) {
                                return;
                            }

                        }
                        int[] d = {data};
                        ByteBuf byteBuf = ByteBufUtil.hexToByteBuf(d);
                        log.info(expression.getDeviceCode() + ":组件状态更新并发送设备动作：" + ByteHexUtil.hexToHexStr(data));
                        channel.writeAndFlush(byteBuf);
                    }
//                }
            }
        }
    }
}
